﻿using System;
using System.Diagnostics;
using System.Runtime.CompilerServices;

namespace agree
{
	public static class EdgeFlag
	{
		public static readonly Edge.Flag EmptyString = Edge.Flag.EtmStringValue;

		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		public static bool IsNonStringType(this Edge.Flag f) { return ((uint)f & 0x18000000) == 0; }
		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		public static bool IsStringFamilyType(this Edge.Flag f) { return (f & Edge.Flag.EtmString) != 0; }
		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		public static bool IsStringFamilyValue(this Edge.Flag f) { return (f & Edge.Flag.EtmStringValue) == Edge.Flag.EtmStringValue; }
		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		public static bool IsTheStringType(this Edge.Flag f) { return (f & Edge.Flag.IdResolve) == Edge.Flag.EtmString; }
		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		public static bool IsStringValue(this Edge.Flag f) { return (((uint)f & 0x7FFFFFFF) ^ 0x50FFFFFF) < (uint)0x00FFFFFF; }
		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		public static bool IsSkolemValue(this Edge.Flag f) { return (((uint)f & 0x7FFFFFFF) ^ 0x57000000) < (uint)0x07000000; }

		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		public static bool IsLeafType(this Edge.Flag f) { return (f & Edge.Flag.EtmLeafType) != 0; }
		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		public static bool HasFeatures(this Edge.Flag f) { return (f & Edge.Flag.EtmNonBareType) != 0; }

		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		public static bool IsIntegerValue(this Edge.Flag f) { return (f & Edge.Flag.EtmInteger) != 0; }
		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		public static int GetIntegerValue(this Edge.Flag f)
		{
			Debug.Assert(f.IsIntegerValue());
			return (int)(f & Edge.Flag.MultiIdMask);
		}
		public static Edge.Flag CreateIntegerValue(int value, bool f_coref = false)
		{
			if ((uint)value > (uint)Edge.Flag.MultiIdMask)
				throw new ArgumentOutOfRangeException(String.Format("Maximum permitted integer value is {0}", (int)Edge.Flag.MultiIdMask));
			Edge.Flag f = Edge.Flag.EtmIntegerValue | (Edge.Flag)value;
			if (f_coref)
				f |= Edge.Flag.Coreference;
			return f;
		}
	};

	[DebuggerDisplay("{ToString(),nq}")]
	public partial struct Edge : IEquatable<Edge>
	{
		public Edge(Flag force_flagsid, int force_mark)
		{
			if (((force_flagsid & Edge.Flag.NeedMark) == 0) != (force_mark == 0))
				throw new Exception();

			this.FlagsId = force_flagsid;
			this.Mark = force_mark;
		}
#if DEBUG
		[DebuggerDisplay("{Edge.TypeInfoFromFlag(Grammar.DebugGrammar.ftm,this.FlagsId, true),nq}")]
#endif
		public readonly Flag FlagsId;

#if DEBUG
		[DebuggerDisplay("{Edge.FormatMark(this),nq}")]
#endif
		public Int32 Mark;

#if DEBUG
		[DebuggerDisplay("{Edge.TypeInfoFromFlag(Grammar.DebugGrammar.ftm, this, true),nq}")]
#endif
		[Flags]
		public enum Flag
		{
			Coreference /*	*/  = unchecked((int)0x80000000),
			EtmLeafType /*					*/ = 0x40000000,
			EtmNonBareType /*				*/ = 0x20000000,
			EtmString /*					*/ = 0x10000000,
			EtmInteger /*					*/ = 0x08000000,
			type_type /*					*/ = 0x70000000,

			EtmIntegerValue /*				*/ = 0x58000000,
			EtmStringValue /*				*/ = 0x50000000,
			EtmSkolemValue /*				*/ = 0x50000000,	// plus.. subtype_*  (required)

			subtype_id_mask /*				*/ = 0x07000000,
			subid_mask /*					*/ = 0x00FFFFFF,
			MultiIdMask /*					*/ = 0x07FFFFFF,	//subtype_id_mask | subid_mask

			NeedMark /*						*/ = Coreference | EtmNonBareType,

			IdResolve /*					*/ = 0x7FFFFFFF,
			Bottom /*						*/ = -1,

			MrsVarTypeH /*					*/ = 0x01000000,
			MrsVarTypeE /*					*/ = 0x02000000,
			MrsVarTypeX /*					*/ = 0x03000000,
			MrsVarTypeU /*					*/ = 0x04000000,
			MrsVarTypeI /*					*/ = 0x05000000,
			MrsVarTypeD /*					*/ = 0x06000000,
			MrsVarTypeP /*					*/ = 0x07000000,
		};

		public static Edge Bottom = new Edge(Flag.Bottom, -1);

		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		public bool IsCoreferenced
		{
			[MethodImpl(MethodImplOptions.AggressiveInlining), DebuggerStepThrough]
			get { return FlagsId < 0; }
		}

		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		public override int GetHashCode() { return Mark + (int)FlagsId; }

		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		public bool Equals(Edge other) { return Mark == other.Mark && FlagsId == other.FlagsId; }

#if !DEBUG
		public String ToString(FeatMgr ftm, bool _not_used)
		{
			return ftm.tu.GetEdgeType(FlagsId).Name;
		}
#endif
	};
}
